/*
 * DIM-SUM操作系统 - 多核调度
 *
 * Copyright (C) 2023 国科础石(重庆)软件有限公司
 *
 * 作者: Dong Peng <w-pengdong@kernelsoft.com>
 *
 * License terms: GNU General Public License (GPL) version 3
 *
 */

#include <dim-sum/devtree.h>
#include <dim-sum/cache.h>
#include <dim-sum/cpu.h>
#include <dim-sum/cpumask.h>
#include <dim-sum/delay.h>
#include <dim-sum/errno.h>
#include <dim-sum/idle.h>
#include <dim-sum/init.h>
#include <dim-sum/percpu.h>
#include <dim-sum/psci.h>
#include <dim-sum/sched.h>
#include <dim-sum/smp.h>

#include <asm/asm-offsets.h>
#include <asm/cacheflush.h>
#include <asm/mmu_context.h>
#include <asm/tlbflush.h>
#include <asm/devtree.h>

extern unsigned long boot_cpu_hartid;

void (*__smp_raise_ipi)(const struct cpumask *, unsigned int);

unsigned long __cpuid_to_hartid_map[MAX_CPUS] = {
	[0 ... MAX_CPUS-1] = INVALID_HARTID
};


void riscv_cpuid_to_hartid_mask(const struct cpumask *in, struct cpumask *out)
{
	int cpu;

	cpumask_clear(out);
	for_each_cpu(cpu, in)
		cpumask_set_cpu(cpuid_to_hartid_map(cpu), out);
}

void __init smp_setup_processor_id(void)
{
	cpuid_to_hartid_map(0) = boot_cpu_hartid;
}


void __init smp_set_raise_ipi_call(void (*fn)(const struct cpumask *,
		unsigned int))
{
	__smp_raise_ipi = fn;
}

void do_IPI(int ipinr, struct exception_spot *regs)
{
	pr_debug("do_IPI, irq is %d, cpu is %d.\n",
			ipinr, smp_processor_id());
	switch (ipinr) {
	case IPI_RESCHEDULE:
		/* 暂不支持 */
		break;
	case IPI_CALL_FUNC:
		/* 暂不支持 */
		break;
	case IPI_SHUTDOWN:
		/* 关闭核心 */
		break;
	default:
		pr_err("unhanded IPI nr %d", ipinr);
		break;
	}
}

void riscv_software_interrupt(void)
{
	/* Clear pending IPI */
	csr_clear(CSR_IP, IE_SIE);
	WARN("No handle %s !!\n", __FUNCTION__);
}

void __init init_smp(void)
{
	bool found_boot_cpu = false;
	unsigned long dt_hartid;
	u32 i = 0, nr = 0;
	int cpuid = 1;

	nr = get_dt_cpu_hartid_nr();
	for (i = 0; i < nr; i++) {
		get_dt_cpu_hartid(i, &dt_hartid);
		if (dt_hartid == ~0UL)
			continue;

		if (dt_hartid == cpuid_to_hartid_map(0)) {
			BUG_ON(found_boot_cpu);
			found_boot_cpu = 1;
			continue;
		}
		if (cpuid >= MAX_CPUS) {
			pr_warn("Invalid cpuid [%d] for hartid [%d]\n",
				cpuid, dt_hartid);
			break;
		}
		cpuid_to_hartid_map(cpuid) = dt_hartid;
		cpuid++;
	}

	BUG_ON(!found_boot_cpu);

	if (cpuid > MAX_CPUS)
		pr_warn("Total number of cpus [%d] is greater than nr_cpus option value [%d]\n",
			cpuid, MAX_CPUS);

	for (cpuid = 1; cpuid < MAX_CPUS; cpuid++) {
		if (cpuid_to_hartid_map(cpuid) != INVALID_HARTID) {
			mark_cpu_possible(cpuid, true);
		}
	}
}

void __init init_slave_early(void)
{
	unsigned int cpu;
	init_smp();

	for_each_possible_cpu(cpu) {
		mark_cpu_present(cpu, true);
	}
}